package decoder;

import ffmpeg.EventDecoderRunnable;
import rtmp.*;
import user.Publish;
import user.PublishGroup;
import user.Receive;
import user.ReceiveGroup;
import util.Common;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.EventLoopGroup;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.apache.log4j.Logger;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class ClientRtmpDecoder extends ByteToMessageDecoder {

    static Logger logger = Logger.getLogger(RtmpPacket.class);

    //  chunk message 数据
    private ByteBuf byteBuf = null;

    private int count = 0;

    //握手数据
    private RtmpHandshake rtmpHandshake = new RtmpHandshake();

    private String path = null;

    RtmpAudio rtmpAudio = new RtmpAudio();

    AudioStreamDecoder audioStreamDecoder = new AudioStreamDecoder();

    private RtmpPacket rtmpPacket = null;

    private int chunkLength = Common.DEFAULT_CHUNK_MESSAGE_LENGTH;

    private EventLoopGroup eventLoopGroup;
    private EventDecoderRunnable eventDecoderRunnable;

    public ClientRtmpDecoder(EventLoopGroup eventLoopGroup){
        this.eventLoopGroup = eventLoopGroup;
    }


    /**
     * 发送链接
     * @param ctx
     */
    private void sendConnect(ChannelHandlerContext ctx){
        RtmpRequestControl rtmpRequestControl = new RtmpRequestControl(ctx, this.chunkLength);
        try {
            rtmpRequestControl.connect();
            rtmpRequestControl.play();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        byteBuf = in;
        if (!rtmpHandshake.isHandshake) { //rtmp 握手认证
            rtmpHandshake.handClientShake(ctx,in);
            if (rtmpHandshake.isHandshake) sendConnect(ctx);
        } else {
            handChunkMessage(ctx);
        }



    }

//      +-------+     +--------------+----------------+
//              | Chunk |  =  | Chunk Header |   Chunk Data   |
//      +-------+     +--------------+----------------+
//
//      +--------------+     +-------------+----------------+-------------------+
//              | Chunk Header |  =  | Basic header| Message Header |Extended Timestamp |
//      +--------------+     +-------------+----------------+-------------------+
//
    /**
     * 解析chunkMessage 数据
     *
     * @param ctx
     */
    private void handChunkMessage(final ChannelHandlerContext ctx) {
        do {

//        logger.info(ctx + " === "+this.byteBuf.readableBytes());
            if(rtmpPacket != null && rtmpPacket.isLackMessage()) {
                rtmpPacket.lackMessage(byteBuf);
            } else if(rtmpPacket != null){
                rtmpPacket.reset(byteBuf,ctx);
            }
            else {
                rtmpPacket = new RtmpPacket(ctx,byteBuf);
            }
            if(rtmpPacket.isLackMessage()){
                return;
            }

            if(!rtmpPacket.isSubpackage()) {
                switch (rtmpPacket.getMessageType()) {
                    case ChunkSize:
                        RtmpChunkSize rtmpChunkSize = new RtmpChunkSize();
                        rtmpChunkSize.setChunkSize(this.chunkLength);
                        rtmpChunkSize.parseChunkSize(rtmpPacket.getMessageData());
                        RtmpResponse.responseChunkSize(ctx,rtmpChunkSize.getChunkSize(),rtmpChunkSize.getChunkSize());
                        rtmpPacket.setChunkLength(rtmpChunkSize.getChunkSize());
                        chunkLength = rtmpChunkSize.getChunkSize();
                        break;
                    case BYTES_READ:
                        RtmpBytesRead rtmpBytesRead = new RtmpBytesRead();
                        rtmpBytesRead.setBytesRead(rtmpPacket.getMessageData());
                        break;
                    case PING:
                        RtmpPing rtmpPing = new RtmpPing();
                        rtmpPing.setPing(rtmpPacket.getMessageData());
                        break;
                    case SERVER_WINDOW:
                        RtmpServerWindow rtmpServerWindow = new RtmpServerWindow();
                        rtmpServerWindow.setRtmpServerWindow(rtmpPacket.getMessageData());
                        break;
                    case AUDIO:
                        rtmpAudio.setAudioData(rtmpPacket.getMessageData(),this.path,rtmpPacket.getRenderTimestamp() ,audioStreamDecoder);
                        break;
                    case VIDEO:
                        RtmpVideo rtmpVideo = new RtmpVideo();
                        rtmpVideo.setVideoData(rtmpPacket.getMessageData(),this.path,rtmpPacket.getRenderTimestamp());
                        break;
                    case NOTIFY:
                        RtmpNotify rtmpNotify = new RtmpNotify();
                        rtmpNotify.setNotify(rtmpPacket.getMessageData(),this.path);
                        if(rtmpNotify.getData() != null) {
                            Publish publish = PublishGroup.getChannel(path);
                            if (publish != null) {
                                publish.MetaData = rtmpNotify.getData();
                            }
                        }
                        break;
                    case Control:
                        RtmpControl rtmpControl = new RtmpControl();
                        rtmpControl.setControl(rtmpPacket.getMessageData(),ctx,chunkLength);
                        logger.info("strameId === " + rtmpPacket.getStreamId());
                        if(rtmpControl.getMsg().equals("FCPublish")){
                            this.path = rtmpControl.getPath();
                            Publish client = new Publish();
                            client.path = path;
                            client.publish = ctx;
                            client.chunk_size = chunkLength; //客户端上来的 chunkLength 大小
                            PublishGroup.setChannel(path, client);
                        } else if(rtmpControl.getMsg().equals("play")) {
                            this.path = rtmpControl.getPath();
                            Receive receive = new Receive();
                            receive.receive = ctx;
                            receive.playing = true;
                            receive.ready = false;
                            receive.keyframe = false;
                            receive.check_size = chunkLength;
                            logger.error("chunkLength == " + chunkLength);
                            ReceiveGroup.setChannel(path, receive);
                            rtmpPacket.type = 1;
                        } else if (rtmpControl.getMsg().equals("connect")) {
                        RtmpResponse.responseChunkSize(ctx,4096,chunkLength);
                        this.chunkLength = 4096;
                        rtmpPacket.setChunkLength(this.chunkLength);
                        } else if (rtmpControl.getMsg().equals("FCUnpublish")) {
                            logger.info("推流结束");
                            ctx.close();
                        }
                        break;
                    case NOT:
                        logger.info("消息类型还没编写");
                        RtmpNot rtmpNot = new RtmpNot();
                        rtmpNot.setNot(rtmpPacket.getMessageData());
                        ctx.close();
                        byteBuf.release();
                        return;
                }
            }
        }while (rtmpPacket.getReadIndex() > 0 && rtmpPacket.getByteBuf().refCnt() > 0);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.err.println("错误信息");
        logger.info(cause.getMessage());
        ctx.close();
        List<Receive> listVideo = ReceiveGroup.getChannel(this.path);
        List<Receive> newListVideo = new ArrayList<Receive>();
        for (int i = 0; i < listVideo.size(); i++) {
            Receive receive = listVideo.get(i);
            if (receive.receive != ctx) {
                newListVideo.add(receive);
            }
        }
        ReceiveGroup.setChannel(this.path, newListVideo);
        super.exceptionCaught(ctx, cause);
    }

}